1 module targets.windows;
2 version(Windows):
3 import commons;
4 import std.windows.registry;
5 static import std.file;
6 
7 bool hasVCRuntime140()
8 {
9 	string arch = "X64";
10 	version(Win32) arch = "X32";
11 	Key currKey = windowsGetKeyWithPath("SOFTWARE", "WOW6432Node", "Microsoft", "VisualStudio", "14.0", "VC", "Runtimes", arch);
12 	return currKey.getValue("Installed").value_DWORD == 1;
13 }
14 
15 pragma(lib, "ole32.lib");
16 pragma(lib, "oleaut32.lib");
17 bool hasMSVCLinker()
18 {
19 	import core.sys.windows.winbase;
20 	import core.sys.windows.winnt;
21 	import core.sys.windows.com;
22 	import core.sys.windows.wtypes;
23 	immutable supportedPre2017Versions = ["14.0"];
24 	const GUID iid_SetupConfiguration = { 0x177F0C4A, 0x1CD3, 0x4DE7, [ 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D ] };
25 
26 	static interface ISetupInstance : IUnknown
27 	{
28 		// static const GUID iid = uuid("B41463C3-8866-43B5-BC33-2B0676F7F42E");
29 		static const GUID iid = { 0xB41463C3, 0x8866, 0x43B5, [ 0xBC, 0x33, 0x2B, 0x06, 0x76, 0xF7, 0xF4, 0x2E ] };
30 
31 		int GetInstanceId(BSTR* pbstrInstanceId);
32 		int GetInstallDate(LPFILETIME pInstallDate);
33 		int GetInstallationName(BSTR* pbstrInstallationName);
34 		int GetInstallationPath(BSTR* pbstrInstallationPath);
35 		int GetInstallationVersion(BSTR* pbstrInstallationVersion);
36 		int GetDisplayName(LCID lcid, BSTR* pbstrDisplayName);
37 		int GetDescription(LCID lcid, BSTR* pbstrDescription);
38 		int ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath);
39 	}
40 
41 	static interface IEnumSetupInstances : IUnknown
42 	{
43 		// static const GUID iid = uuid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848");
44 
45 		int Next(ULONG celt, ISetupInstance* rgelt, ULONG* pceltFetched);
46 		int Skip(ULONG celt);
47 		int Reset();
48 		int Clone(IEnumSetupInstances* ppenum);
49 	}
50 
51 	static interface ISetupConfiguration : IUnknown
52 	{
53 		// static const GUID iid = uuid("42843719-DB4C-46C2-8E7C-64F1816EFD5B");
54 		static const GUID iid = { 0x42843719, 0xDB4C, 0x46C2, [ 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B ] };
55 
56 		int EnumInstances(IEnumSetupInstances* ppEnumInstances) ;
57 		int GetInstanceForCurrentProcess(ISetupInstance* ppInstance);
58 		int GetInstanceForPath(LPCWSTR wzPath, ISetupInstance* ppInstance);
59 	}
60 
61 
62 	if("VSINSTALLDIR" in environment && !("LDC_VSDIR_FORCE" in environment))
63 	{
64 		if(!("VSCMD_ARG_TGT_ARCH" in environment))
65 			return true;
66 		string tgtArch = environment["VSCMD_ARG_TGT_ARCH"];
67 		if(tgtArch == "x64" || tgtArch == "x32")
68 			return true;
69 	}
70 	if("LDC_VSDIR" in environment && std.file.exists(environment["LDC_VSDIR"]))
71 		return true;
72 
73 	bool detectVSInstallDirViaCOM()
74 	{
75 		import core.sys.windows.windows;
76 		import core.stdc.wchar_:wcscmp;
77 
78 		CoInitialize(null); scope(exit) CoUninitialize();
79 
80 		ISetupConfiguration setup;
81     	IEnumSetupInstances instances;
82 		ISetupInstance instance;
83 		DWORD fetched;
84 
85 		HRESULT hr = CoCreateInstance(&iid_SetupConfiguration, null, CLSCTX_ALL, &ISetupConfiguration.iid, cast(void**) &setup);
86 		if(hr != S_OK || !setup) return false;
87 
88 		scope(exit) setup.Release();
89 		if(setup.EnumInstances(&instances) != S_OK)
90 			return false;
91 		scope(exit) instances.Release();
92 
93 		BSTR thisVersionString, thisInstallDir;
94 		while (instances.Next(1, &instance, &fetched) == S_OK && fetched)
95 		{
96 			if(instance.GetInstallationVersion(&thisVersionString) != S_OK || 
97 			instance.GetInstallationPath(&thisInstallDir) != S_OK)
98 				continue;
99 			return true;
100 		}
101 		return false;
102 	}
103 
104 	if(detectVSInstallDirViaCOM())
105 		return true;
106 
107 	if(Key k = windowsGetKeyWithPath("SOFTWARE", "Microsoft", "VisualStudio", "SxS", "VS7"))
108 	if(k.getValue("15.0").value_SZ)
109 		return true;
110 	
111 	foreach (ver; supportedPre2017Versions)
112 	{
113 		Key k = windowsGetKeyWithPath("SOFTWARE", "Microsoft", "VisualStudio", ver);
114 		try
115 		{
116 			if(k !is null && k.getValue("InstallDir"))
117 				return true;
118 		}
119 		catch(Exception e){return false;}
120 	}
121 	return false;
122 }
123 
124 private string getVCDownloadLink()
125 {
126 	version(Win64) return "https://aka.ms/vs/17/release/vc_redist.x64.exe";
127 	else version(Win32) return "https://aka.ms/vs/17/release/vc_redist.x86.exe";
128 	else version(AArch64) return "https://aka.ms/vs/17/release/vc_redist.arm64.exe";
129 }
130 
131 private bool installVCRuntime140(ref Terminal t, ref RealTimeConsoleInput input)
132 {
133 	string vcredist = buildNormalizedPath(std.file.getcwd(), "buildtools", "vcredist.exe");
134 	if(!downloadFileIfNotExists("Get Microsoft Visual C++ Redistributable for being able to compile D Programming Language", getVCDownloadLink, vcredist, t, input))
135 	{
136 		t.writelnError("Needs to download VCRuntime.");
137 		return false;
138 	}
139 	t.writelnHighlighted("Installing Microsoft Visual C++ Redistributable");
140 
141 	auto ret = wait(spawnShell(vcredist~" /install /quiet /norestart")) == 0;
142 	if(ret)
143 		t.writelnSuccess("Successfully installed Microsoft Visual C++ Redistributable.");
144 	else 
145 		t.writelnError("Could not install Microsoft Visual C++ Redistributable.");
146 	return ret;
147 }
148 
149 private bool installMSLinker(ref Terminal t, ref RealTimeConsoleInput input)
150 {
151 	string vcBuildTools = buildNormalizedPath(std.file.getcwd(), "buildtools", "vc_BuildTools.exe");
152 	enum vcBuildToolsLink = "https://aka.ms/vs/17/release/vs_BuildTools.exe";
153 	if(!downloadFileIfNotExists("Get Windows SDK for being able  to compile D programming language", vcBuildToolsLink, vcBuildTools, t, input))
154 	{
155 		t.writelnError("Need to download vs_BuildTools.exe");
156 		return false;
157 	}
158 	t.writelnHighlighted("Starting Windows SDK Installation.");
159 
160 	string[] installList = 
161 	[
162 		"Microsoft.VisualStudio.Workload.VCTools",
163 		"Microsoft.VisualStudio.Component.TestTools.BuildTools",
164 		"Microsoft.VisualStudio.Component.VC.ASAN",
165 		"Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
166 	];
167 
168 	import std.algorithm:reduce;
169 
170 	auto ret = wait(spawnShell(vcBuildTools~" --wait --passive --norestart " ~installList.reduce!((str, last) => "--add "~last~" "~str))) == 0;
171 	return ret == 0;
172 }
173 
174 ChoiceResult prepareWindows(Choice* c, ref Terminal t, ref RealTimeConsoleInput input, in CompilationOptions cOpts)
175 {
176 	if(!hasVCRuntime140)
177 	{
178 		if(!installVCRuntime140(t, input))
179 		{
180 			t.writelnError("Your system must install Microsoft Visual C++ 14 for using the D Programming Language.");
181 			return ChoiceResult.Error;
182 		}
183 	}
184 	if(!hasMSVCLinker)
185 	{
186 		if(!installMSLinker(t, input))
187 		{
188 			if(hasMSVCLinker)
189 				t.writelnSuccess("Succesfully installed Windows SDK.");
190 			else
191 			{
192 				t.writelnError("Could not install Windows SDK.");
193 				return ChoiceResult.Error;
194 			}
195 		}
196 		else
197 		{
198 			t.writelnError("Your system must install the MSLinker. This is important for creating binaries without dependencies.");
199 			return ChoiceResult.Error;
200 		}
201 	}
202 
203 	// waitOperations([{
204 		std.file.chdir(configs["gamePath"].str);
205 		if(timed(() => waitDub(t, DubArguments().command("build").configuration("script").opts(cOpts)) != 0))
206 			return ChoiceResult.Error;
207 			// return false;
208 		// return true;
209 	// },
210 	// {
211 		if(!c.scriptOnly)
212 		{
213 			std.file.chdir(getHipPath);
214 			if(timed(() => waitDub(t, DubArguments().command("build").configuration("script").opts(cOpts))) != 0)
215 				return ChoiceResult.Error;
216 		}
217 		// return true;
218 	// }]);
219 	
220 	wait(spawnShell((getHipPath("bin", "desktop", "hipreme_engine.exe") ~ " "~ configs["gamePath"].str)));
221 
222 	return ChoiceResult.Continue;
223 }